#include "common.h"


void vid_init(struct IVid* vid, int index)
{
    vid->used = 0;
    vid->index = index;
    vid->vsrc = NULL;
    vid->frame_time = 0.0;
    vid->fps = 0.0;
    vid->frame_counter = 0;
    //av
    vid->fc = NULL;
    vid->cc = NULL;
    //vid->pkt;
    vid->video_stream = -1;
    vid->frame_yuv = NULL;
    vid->frame_rgb = NULL;
    vid->buffer = NULL;
    vid->buffer_size = 0;
    //src and dst frame info
    vid->src_width = 0;
    vid->src_height = 0;
    vid->src_format = 0;
    vid->dst_width = 0;
    vid->dst_height = 0;
    vid->dst_format = 0;
    //thread
    //vid->thr;
    vid->thr_running = 0;
    vid->thr_stop = 0;
    //fifo
    vid->fifo_frames = NULL;
    vid->fifo_depth = 0;
    vid->fifo_counter = 0;
    //msg
    vid->msg = VIDMSG_None;
    vid->msg_param = 0;
    pthread_mutex_init(&vid->msg_mutex, NULL);
}

int vid_read_frame(struct IVid* vid, void* data)
{
    for (;;)
    {
        int read_st = av_read_frame(vid->fc, &vid->pkt);
        if (read_st < 0)
        {
            return 0;
        }
        if (vid->pkt.stream_index == vid->video_stream)
        {
            int full = 0;
            int dec_st = avcodec_decode_video(vid->cc, vid->frame_yuv, &full,
                                              vid->pkt.data, vid->pkt.size);
            if (full)
            {
                //update frame data
                img_convert((AVPicture*)vid->frame_rgb, vid->dst_format,
                            (AVPicture*)vid->frame_yuv, vid->src_format,
                            vid->src_width, vid->src_height,
                            vid->dst_width, vid->dst_height);
                av_free_packet(&vid->pkt);
                break;
            }
        }
    }
    //format rgba data
    int y;
    uint8_t* ptr = data;
    AVFrame* pFrame = vid->frame_rgb;
    for (y = 0; y < vid->dst_height; ++y)
    {
        memcpy(ptr, pFrame->data[0]+(vid->dst_height-y-1)*pFrame->linesize[0], vid->dst_width*4);
        ptr +=vid->dst_width*4;
    }
    return 1;
}

//
// stream
//

void stream_start(struct IVid* vid)
{
    vid->thr_stop = 0;
    pthread_create(&vid->thr, NULL, stream_running, vid);
}

void stream_stop(struct IVid* vid)
{
    vid->thr_stop = 1;
}

void* stream_running(void* arg)
{
    struct IVid* vid = (struct IVid*)arg;
    int counter = 0;

    vid->thr_running = 1;

    for (;;)
    {
        struct IVidFrame* frm = &vid->fifo_frames[counter];

        //wait for writing frame
        for (;;)
        {
            if (vid->thr_stop)
                break;
            if (sem_trywait(&frm->write_ok) == 0)
                break;
            usleep(120);
        }

        //end streaming
        if (vid->thr_stop)
            break;

        //messages
        pthread_mutex_lock(&vid->msg_mutex);
        switch (vid->msg)
        {
        }
        pthread_mutex_unlock(&vid->msg_mutex);

        //read frame
        if (vid_read_frame(vid, frm->data) == 0)
        {
            //vidRewind(vid->vsrc);
            av_seek_frame(vid->fc, vid->video_stream, 0, AVSEEK_FLAG_BYTE);
            vid_read_frame(vid, frm->data);
        }

        sem_post(&frm->read_ok);
        counter++;
        if (counter == vid->fifo_depth)
            counter = 0;
    }

    vid->thr_running = 0;

    pthread_exit(NULL);
}

//
// utils
//

void img_convert(AVPicture *dst, int dst_pix_fmt, const AVPicture *src, int pix_fmt,
                int src_w, int src_h, int dst_w, int dst_h)
{
    int in_format = pix_fmt;
    int out_format = dst_pix_fmt;

    AVFrame *pFrameYUV = (AVFrame*)src;
    AVFrame *pFrameRGB = (AVFrame*)dst;

    struct SwsContext *img_convert_ctx;
    img_convert_ctx = sws_getContext(src_w, src_h, in_format, dst_w, dst_h, out_format, SWS_FAST_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
    #ifdef __WIN32__
    sws_scale(img_convert_ctx,
              (const uint8_t* const*) pFrameYUV->data, pFrameYUV->linesize, 0, src_h,
              (uint8_t* const*)pFrameRGB->data, pFrameRGB->linesize);
    #else
    sws_scale(img_convert_ctx,
              (uint8_t**) pFrameYUV->data, pFrameYUV->linesize, 0, src_h,
              (uint8_t**)pFrameRGB->data, pFrameRGB->linesize);
    #endif
    sws_freeContext(img_convert_ctx);
}

void convert_frame_data(void* data_OUT, int width, int height, int bpp, void* data_IN)
{
    int in_format = PIX_FMT_YUV420P;
    int out_format = PIX_FMT_RGBA;

    AVFrame *pFrameYUV = NULL;
    pFrameYUV = avcodec_alloc_frame();
    avpicture_fill((AVPicture *) pFrameYUV, (uint8_t*) data_IN, in_format, width, height);

    AVFrame *pFrameRGB = NULL;
    pFrameRGB = avcodec_alloc_frame();
    avpicture_fill((AVPicture *) pFrameRGB, data_OUT, out_format, width, height);

    struct SwsContext *img_convert_ctx;
    img_convert_ctx = sws_getContext(width, height, in_format, width, height, out_format, SWS_FAST_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
    #ifdef __WIN32__
    sws_scale(img_convert_ctx,
              (const uint8_t* const*)pFrameYUV->data, pFrameYUV->linesize, 0, height,
              (uint8_t* const*)pFrameRGB->data, pFrameRGB->linesize);
    #else
    sws_scale(img_convert_ctx,
              (uint8_t**)pFrameYUV->data, pFrameYUV->linesize, 0, height,
              (uint8_t**)pFrameRGB->data, pFrameRGB->linesize);
    #endif
    sws_freeContext(img_convert_ctx);
}

int min_pow2(int x)
{
    int m = 0;
    int y = 2;
    while (y <= x)
    {
        m = y;
        y *= 2;
    }
    return m;
}


